Contents

JVM加载class文件

  1. 当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
  2. 当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
  3. 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
  4. 若ExtClassLoader也加载失败,则会使用AppClassLoader来加载
  5. 如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException

类加载详细过程

加载器加载到jvm中,接下来其实又分了好几个步骤:

  • 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象。
  • 连接,连接又包含三块内容:验证、准备、初始化。
    1)验证,文件格式、元数据、字节码、符号引用验证;
    2)准备,为类的静态变量分配内存,并将其初始化为默认值;
    3)解析,把类中的符号引用转换为直接引用。
  • 初始化,为类的静态变量赋予正确的初始值。

JIT即时编辑器

一般我们可能会想:JVM在加载了这些class文件以后,针对这些字节码,逐条取出,逐条执行–>解析器解析
但如果是这样的话,那就太慢了!

我们的JVM是这样实现的:

就是把这些Java字节码重新编译优化,生成机器码,让CPU直接执行。这样编出来的代码效率会更高。
编译也是要花费时间的,我们一般对热点代码做编译,非热点代码直接解析就好了。

使用热点探测来检测是否为热点代码,热点探测有两种方式:

  • 采样
  • 计数器

目前HotSpot使用的是计数器的方式,它为每个方法准备了两类计数器:

  • 方法调用计数器(Invocation Counter)
  • 回边计数器(Back EdgeCounter)。

在确定虚拟机运行参数的前提下,这两个计数器都有一个确定的阈值,当计数器超过阈值溢出了,就会触发JIT编译

JVM的内存结构

  • 堆:存放对象实例,几乎所有的对象实例都在这里分配内存
  • 虚拟机栈:虚拟机栈描述的是Java方法执行的内存结构:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息
  • 本地方法栈:本地方法栈则是为虚拟机使用到的Native方法服务。
  • 方法区:存储已被虚拟机加载的类元数据信息(元空间)
  • 程序计数器:当前线程所执行的字节码的行号指示器

常量池

常量池(Constant PoolTable),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放

常量池存储的是:

  • 字面量(Literal):文本字符串等—->用双引号引起来的字符串字面量都会进这里面
  • 符号引用(Symbolic References)
  • 类和接口的全限定名(Full Qualified Name)
  • 字段的名称和描述符(Descriptor)
  • 方法的名称和描述符

运行时常量池只是换了一个位置(原本来方法区,现在在堆中),但可以明确的是:类加载后,常量池中的数据会在运行时常量池中存放

字符串常量池:

HotSpot VM里,记录interned string的一个全局表叫做StringTable,它本质上就是个HashSet。注意它只存储对java.lang.String实例的引用,而不存储String对象的内容。

字符串常量池只存储引用,不存储内容
intern方法:

  • 如果常量池中存在当前字符串,那么直接返回常量池中它的引用。
  • 如果常量池中没有此字符串, 会将此字符串引用保存到常量池中后, 再直接返回该字符串的引用!

GC垃圾回收
回收算法

  • 标记-清除算法
  • 复制算法
  • 标记-整理算法
  • 分代收集算法

垃圾收集算法只能算是方法论,落地实现的是垃圾收集器

  • Serial收集器
  • ParNew收集器
  • Parallel Scavenge收集器
  • Serial Old收集器
  • Parallel Old收集器
  • CMS收集器
  • G1收集器

实际中大部分收集器是可以互相组合使用的

JVM参数与调优
做过JavaWeb项目(ssh/ssm),遇到过OutOfMemory这样的错误。一般解决起来也很方便,在启动的时候加个参数就行了。内存的分配的大小,使用收集器,这些都可以由我们根据需求,现实情况来指定。

Contents